#include "download_client.h"

#include <winhttp.h>
#include <stdlib.h>

struct download_client_t {
	gequations_t * equs;
	peer_info_t * peers;
	int peernum;
	int runnable;
};

download_client_t * create_download_client(gequations_t * equations, peer_info_t * peers, int peernum)
{
	download_client_t * client = (download_client_t *) malloc(sizeof(download_client_t));

	client->equs = equations;
	client->peernum = peernum;
	client->peers = peers;
	client->runnable = 0;

	return client;
}

void destroy_download_client(download_client_t * client)
{
	free(client);
}

void download_client_refreshpeer(download_client_t * client, peer_info_t * peers, int peernum)
{
	client->peernum = 0;
	client->peers = peers;
	client->peernum = peernum;
}

#define NUMBER_LEN_MAX 30

BOOL readData(HINTERNET hRequest, DWORD bytes, char * buffer)
{
	DWORD bRead;
	while (bytes>0) {
		if (!WinHttpReadData(hRequest, (LPVOID) buffer, bytes, &bRead)) {
			return FALSE;
		}
		bytes -= bRead;
		buffer += bRead;
	}
	return TRUE;
}

int query_server(LPWSTR addr, INTERNET_PORT port, int * checksum, gf16_t * coefs)
{
	HINTERNET hSession, hConnect, hRequest;
	BOOL bResult;
	WCHAR header[2048];
	int header_len;
	int index = -1;
	DWORD bytes, bytes_read;

    hSession = WinHttpOpen(L"BitGene Download Client/0.1", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
    if (hSession) hConnect = WinHttpConnect(hSession, addr, port, 0);
	if (hConnect) hRequest = WinHttpOpenRequest(hConnect, L"GET", L"/QUERY", NULL, NULL, NULL, 0);
	if (hRequest) {
		header_len = swprintf_s(header, 2048, L"CHECKSUM: %d\r\n", *checksum);
		bResult = WinHttpSendRequest(hRequest, header, header_len, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
		if (bResult) bResult = WinHttpReceiveResponse(hRequest, NULL);
		if (bResult) {
			header_len = 2000;
			bResult = WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, L"CHECKSUM", header, &header_len, WINHTTP_NO_HEADER_INDEX);
			if (bResult) {
				header[header_len] = L'\0';
				(*checksum) |= _wtoi(header)<<16;
				header_len = 2000;
				bResult = WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, L"INDEX", header, &header_len, WINHTTP_NO_HEADER_INDEX);
				if (bResult) {
					header[header_len] = L'\0';
					index = _wtoi(header);
					header_len = 2000;
					bResult = WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CONTENT_LENGTH, WINHTTP_HEADER_NAME_BY_INDEX, header, &header_len, WINHTTP_NO_HEADER_INDEX);
					if (bResult) {
						header[header_len] = L'\0';
						bytes = _wtoi(header);
						bResult = WinHttpReadData(hRequest, coefs, bytes, &bytes_read);
					}
				}
			}
		}
	}

    if (hRequest) WinHttpCloseHandle(hRequest);
    if (hConnect) WinHttpCloseHandle(hConnect);
    if (hSession) WinHttpCloseHandle(hSession);

	return bResult ? index : -1;
}

BOOL fetch_server(LPWSTR addr, INTERNET_PORT port, int checksum, int index, gf16block_t block)
{
	HINTERNET hSession, hConnect, hRequest;
	BOOL bResult;
	WCHAR header[2048];
	int header_len;
	DWORD bytes, bytes_read;

    hSession = WinHttpOpen(L"BitGene Download Client/0.1", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
    if (hSession) hConnect = WinHttpConnect(hSession, addr, port, 0);
	if (hConnect) hRequest = WinHttpOpenRequest(hConnect, L"GET", L"/FETCH", NULL, NULL, NULL, 0);
	if (hRequest) {
		header_len = swprintf_s(header, 2048, L"CHECKSUM: %d\r\n", checksum);
		bResult = WinHttpAddRequestHeaders(hRequest, header, header_len, 0);
		header_len = swprintf_s(header, 2048, L"INDEX: %d\r\n", index);
		bResult = WinHttpAddRequestHeaders(hRequest, header, header_len, 0);
		bResult = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
		if (bResult) bResult = WinHttpReceiveResponse(hRequest, NULL);
		if (bResult) {
			header_len = 2000;
			bResult = WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CONTENT_LENGTH, WINHTTP_HEADER_NAME_BY_INDEX, header, &header_len, WINHTTP_NO_HEADER_INDEX);
			if (bResult) {
				header[header_len] = L'\0';
				bytes = _wtoi(header);
				bResult = WinHttpReadData(hRequest, block, bytes, &bytes_read);
			}
		}
	}

    if (hRequest) WinHttpCloseHandle(hRequest);
    if (hConnect) WinHttpCloseHandle(hConnect);
    if (hSession) WinHttpCloseHandle(hSession);

	return bResult;
}

void download_client_run(download_client_t * client, int maxconn, size_t total)
{
	char * peer_addr, * peer_port;
	int peer_addr_len, peer_port_len;

	int index;
	int checksum;
	int rResult;
	int i;
	gf16_t gg;

	size_t got = 0;
	
	WCHAR addr[2048];
	char s_port[60];
	INTERNET_PORT port;

	gf16_t * coefs;
	gf16block_t block;
	gequation_t * e;

	printf("Client running...\n");

	client->runnable = 1;
	for (;client->runnable;) {
		if (client->peernum>0) {
			i = rand()%client->peernum;
			peer_addr = client->peers[i].peer_ip;
			peer_addr_len = client->peers[i].peer_ip_length;
			peer_port = client->peers[i].peer_port;
			peer_port_len = client->peers[i].peer_port_length;
			
			strncpy_s(s_port, 60, peer_port, peer_port_len);
			port = (INTERNET_PORT)atoi(s_port);

			_snwprintf_s(addr, 2048, peer_addr_len, L"%S", peer_addr);
			printf("Connecting %S:%d\n", addr, port);

			checksum = rand();
		
			coefs = new_coefs(client->equs);

			index = query_server(addr, port, &checksum, coefs);

			//Debug
					//gg = 0;
					//printf("Coefs:\n");
					//for (i=0; i<256; ++i) {
					//	gg = gf16_add(gg, gf16_mul(1, coefs[i]));
					//	printf("%d ", gg);
					//}
			//
			if (index>=0) {
				e = create_equation(coefs);
				rResult = reduce(client->equs, e);
			} else {
				free(coefs);
				continue;
			}
			if (rResult==2) {
				destroy_equation(e);
				client->runnable = 2;
				break;
			}
			if (rResult==0) {
				block = new_block(client->equs);
				if (fetch_server(addr, port, checksum, index, block)) {
					//Debug
					//printf("\nRhs:\n");
					//for (i=0; i<16; ++i) {
					//	printf("%d ", block[i]);
					//	if (gg!=block[i]) {
					//		printf("Error!\n");
					//	}
					//}
					//printf("\n");
					//

					specify_equ_rhs(e, block);
					rResult = combine(client->equs, e);
					if (rResult==2) {
						break;
					}
					if (rResult!=0) {
						destroy_equation(e);
					} else {
						++ got;
						printf("Got: %d / %d\n", got, total);
					}
				} else {
					destroy_equation(e);
					free(block);
				}
			}
		}
	}
}

void download_client_stop(download_client_t * client)
{
	client->runnable = 0;
}
